import os
import sys
import argparse
import xml.etree.ElementTree as ET
import pandas as pd
import subprocess
import json
import shutil

# --- 配置区 ---

# OpenHarmony 默认的 manifest 入口文件
MANIFEST_ENTRY_FILE = "default.xml"
MANIFESTS_DIR_REL_PATH = ".repo/manifests"

# 输出的 Excel 文件名
OUTPUT_FILE_NAME = "openharmony_code_stats_cloc.xlsx"


# --- 核心功能函数 ---

def check_cloc_installed():
    """检查系统中是否安装了 cloc 命令"""
    if shutil.which("cloc") is None:
        print("错误: 'cloc' 命令未找到。")
        print("请先安装 cloc 工具。您可以访问 https://github.com/AlDanial/cloc 获取安装指南。")
        print("例如，在 Ubuntu/Debian 上运行: sudo apt-get install cloc")
        sys.exit(1)
    print("'cloc' 工具已找到，准备开始分析。")

def _parse_recursive(manifest_dir, file_name, all_projects_set, processed_files_set):
    """
    递归解析 manifest XML 文件。
    :param manifest_dir: manifest 文件所在的根目录 (.repo/manifests)
    :param file_name: 当前要解析的文件名 (相对于 manifest_dir)
    :param all_projects_set: 用于存储所有项目路径的集合 (用于去重)
    :param processed_files_set: 用于防止循环引用的集合
    """
    full_path = os.path.join(manifest_dir, file_name)
    
    if full_path in processed_files_set:
        print(f"  [警告] 检测到循环引用或重复包含，跳过文件: {file_name}")
        return
        
    if not os.path.exists(full_path):
        print(f"  [警告] 包含的 manifest 文件不存在，已跳过: {full_path}")
        return

    print(f"  -> 正在解析: {file_name}")
    processed_files_set.add(full_path)

    try:
        tree = ET.parse(full_path)
        root = tree.getroot()

        # 1. 提取当前文件中的 <project>
        for project in root.findall('project'):
            path = project.get('path')
            if path:
                all_projects_set.add(path)

        # 2. 递归处理 <include>
        for include in root.findall('include'):
            include_name = include.get('name')
            if include_name:
                _parse_recursive(manifest_dir, include_name, all_projects_set, processed_files_set)

    except ET.ParseError as e:
        print(f"错误: 解析 XML 文件 {full_path} 失败: {e}")


def parse_openharmony_manifest(project_root):
    """
    解析 OpenHarmony 的多文件 manifest 结构，提取所有 project 的 path 属性。
    """
    manifests_dir = os.path.join(project_root, MANIFESTS_DIR_REL_PATH)
    entry_file_path = os.path.join(manifests_dir, MANIFEST_ENTRY_FILE)
    
    print(f"正在解析 OpenHarmony Manifest, 入口文件: {entry_file_path}")

    if not os.path.exists(entry_file_path):
        print(f"错误: Manifest 入口文件未找到 at '{entry_file_path}'")
        print("请确保输入的路径是 OpenHarmony 源代码的根目录。")
        sys.exit(1)

    all_projects = set()
    processed_files = set()
    _parse_recursive(manifests_dir, MANIFEST_ENTRY_FILE, all_projects, processed_files)

    # 转换为排序后的列表，保证每次运行结果顺序一致
    sorted_projects = sorted(list(all_projects))
    print(f"\n成功解析 Manifest，共找到 {len(sorted_projects)} 个独立仓库。")
    return sorted_projects


def analyze_repo_with_cloc(repo_full_path):
    """
    使用 cloc 分析单个仓库，返回 (代码总量, 代码 NBNC 总量)。
    代码总量 = blank + comment + code
    代码 NBNC 总量 = code
    """
    if not os.path.isdir(repo_full_path):
        return None  # 仓库目录不存在

    command = ["cloc", "--json", "--quiet", repo_full_path]

    try:
        result = subprocess.run(command, capture_output=True, text=True, check=False)

        if result.returncode != 0:
            return 0, 0

        cloc_data = json.loads(result.stdout)
        summary = cloc_data.get("SUM", {})
        
        nbnc_lines = summary.get("code", 0)
        blank_lines = summary.get("blank", 0)
        comment_lines = summary.get("comment", 0)
        total_lines = nbnc_lines + blank_lines + comment_lines
        
        return total_lines, nbnc_lines

    except Exception as e:
        print(f"  [错误] 在分析仓库 {repo_full_path} 时发生未知错误: {e}")
        return 0, 0


def save_to_excel(results, output_file):
    """将统计结果保存到 Excel 文件"""
    if not results:
        print("没有可供保存的结果。")
        return
        
    print(f"\n正在将结果保存到 {output_file} ...")
    try:
        df = pd.DataFrame(results, columns=['仓库路径', '代码总量', '代码 NBNC 总量'])
        df.to_excel(output_file, index=False, engine='openpyxl')
        print(f"保存成功！文件路径: {os.path.abspath(output_file)}")
    except Exception as e:
        print(f"\n错误: 保存 Excel 文件失败: {e}")


def main():
    """主函数，负责整体流程控制"""
    parser = argparse.ArgumentParser(
        description="使用 cloc 统计 OpenHarmony 项目中各个仓库的代码量。"
    )
    parser.add_argument("project_path", help="OpenHarmony 源代码的根目录路径。")
    args = parser.parse_args()

    # 1. 检查 cloc 是否安装
    check_cloc_installed()

    oh_root = os.path.abspath(args.project_path)
    if not os.path.isdir(oh_root):
        print(f"错误: 提供的路径 '{oh_root}' 不是一个有效的目录。")
        sys.exit(1)

    # 2. 解析 manifest 获取仓库列表
    repo_paths = parse_openharmony_manifest(oh_root)
    
    results = []
    total_repos = len(repo_paths)

    print("\n开始使用 cloc 分析各个仓库的代码量 (这可能需要一些时间)...")
    for i, repo_path in enumerate(repo_paths, 1):
        full_path = os.path.join(oh_root, repo_path)
        
        # 打印进度
        print(f"[{i}/{total_repos}] 正在分析: {repo_path}")

        stats = analyze_repo_with_cloc(full_path)
        
        if stats is None:
            print(f"  [警告] 仓库路径不存在，已跳过: {full_path}")
            continue

        total_lines, nbnc_lines = stats
        results.append({
            '仓库路径': repo_path,
            '代码总量': total_lines,
            '代码 NBNC 总量': nbnc_lines
        })
        print(f"  -> 完成: 总行数={total_lines}, NBNC行数={nbnc_lines}")

    # 4. 保存结果到 Excel
    save_to_excel(results, OUTPUT_FILE_NAME)


if __name__ == "__main__":
    main()
